package com.caipos.netty;

import akka.actor.ActorRef;
import com.caipos.akka.AkkaActorInitializer;
import com.caipos.akka.message.DeviceDataMessage;
import com.caipos.akka.message.RegisterDevice;
import com.caipos.akka.message.DeviceStatusUpdate;
import com.caipos.entity.Device;
import com.caipos.entity.DeviceCommand;
import com.caipos.entity.DeviceData;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.CharsetUtil;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.net.InetSocketAddress;
import java.time.LocalDateTime;

@Component
@RequiredArgsConstructor
@Slf4j
public class UdpServer {

    private final String host;
    private final int port;
    private final AkkaActorInitializer akkaActorInitializer;
    private final ObjectMapper objectMapper;

    private EventLoopGroup group;
    private Channel channel;

    @PostConstruct
    public void start() {
        // 在新线程中启动UDP服务器，避免阻塞Spring上下文初始化
        new Thread(() -> {
            group = new NioEventLoopGroup();
            try {
                Bootstrap b = new Bootstrap();
                b.group(group)
                        .channel(NioDatagramChannel.class)
                        .option(ChannelOption.SO_BROADCAST, true)
                        .handler(new ChannelInitializer<NioDatagramChannel>() {
                            @Override
                            protected void initChannel(NioDatagramChannel ch) {
                                ChannelPipeline pipeline = ch.pipeline();
                                pipeline.addLast(new UdpServerHandler(akkaActorInitializer.getDeviceManagerActor(), objectMapper, UdpServer.this));
                            }
                        });

                // 绑定端口并启动服务器
                ChannelFuture f = b.bind(host, port).sync();
                log.info("UDP Server started on {}:{}", host, port);
                channel = f.channel();

                // 等待服务器关闭
                f.channel().closeFuture().sync();
            } catch (Exception e) {
                log.error("UDP Server error: {}", e.getMessage());
            } finally {
                group.shutdownGracefully();
            }
        }).start();
    }

    @PreDestroy
    public void stop() {
        if (channel != null) {
            channel.close();
        }
        if (group != null) {
            group.shutdownGracefully();
        }
        log.info("UDP Server stopped");
    }

    /**
     * 发送命令到设备
     */
    public boolean sendCommandToDevice(String ip, int port, DeviceCommand command) {
        if (channel == null || !channel.isActive()) {
            log.error("UDP channel is not active");
            return false;
        }

        try {
            // 创建下行消息
            UdpDownlinkMessage downlinkMessage = UdpDownlinkMessage.builder()
                    .type("COMMAND")
                    .commandId(command.getId())
                    .commandType(command.getCommandType())
                    .params(command.getCommandParams())
                    .timestamp(LocalDateTime.now())
                    .build();

            String jsonMessage = objectMapper.writeValueAsString(downlinkMessage);

            // 发送UDP包
            channel.writeAndFlush(new DatagramPacket(
                    Unpooled.copiedBuffer(jsonMessage, CharsetUtil.UTF_8),
                    new InetSocketAddress(ip, port)
            )).addListener((ChannelFutureListener) future -> {
                if (future.isSuccess()) {
                    log.info("Command {} sent to device {} at {}:{}",
                            command.getId(), command.getDeviceId(), ip, port);
                } else {
                    log.error("Failed to send command {} to device {}",
                            command.getId(), command.getDeviceId(), future.cause());
                }
            });

            return true;
        } catch (JsonProcessingException e) {
            log.error("Failed to serialize command: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 发送响应给设备
     */
    public void sendResponse(ChannelHandlerContext ctx, InetSocketAddress sender, String messageType,
                             String status, String message, Object data) {
        try {
            UdpResponse response = UdpResponse.builder()
                    .type(messageType + "_RESPONSE")
                    .status(status)
                    .message(message)
                    .data(data)
                    .timestamp(LocalDateTime.now())
                    .build();

            String jsonResponse = objectMapper.writeValueAsString(response);

            ctx.writeAndFlush(new DatagramPacket(
                    Unpooled.copiedBuffer(jsonResponse, CharsetUtil.UTF_8),
                    sender
            ));
        } catch (JsonProcessingException e) {
            log.error("Failed to serialize response: {}", e.getMessage());
        }
    }

    /**
     * UDP服务器处理器
     */
    @ChannelHandler.Sharable
    @RequiredArgsConstructor
    public static class UdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

        private final ActorRef deviceManagerActor;
        private final ObjectMapper objectMapper;
        private final UdpServer udpServer;

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) {
            try {
                // 解析收到的UDP数据包
                String json = packet.content().toString(CharsetUtil.UTF_8);
                InetSocketAddress sender = packet.sender();
                log.info("Received UDP message from {}:{} - {}",
                        sender.getHostString(), sender.getPort(), json);

                // 反序列化为通用消息
                UdpUplinkMessage message = objectMapper.readValue(json, UdpUplinkMessage.class);

                // 根据消息类型处理
                switch (message.getType()) {
                    case "REGISTER":
                        handleRegistration(ctx, sender, message);
                        break;
                    case "DATA":
                        handleData上报(ctx, sender, message);
                        break;
                    case "HEARTBEAT":
                        handleHeartbeat(ctx, sender, message);
                        break;
                    case "COMMAND_RESPONSE":
                        handleCommandResponse(ctx, sender, message);
                        break;
                    default:
                        log.warn("Unknown message type: {}", message.getType());
                        udpServer.sendResponse(ctx, sender, message.getType(),
                                "ERROR", "Unknown message type", null);
                }
            } catch (Exception e) {
                log.error("Error processing UDP message: {}", e.getMessage());
                udpServer.sendResponse(ctx, packet.sender(), "ERROR",
                        "ERROR", "Failed to process message: " + e.getMessage(), null);
            }
        }

        /**
         * 处理设备注册
         */
        private void handleRegistration(ChannelHandlerContext ctx, InetSocketAddress sender, UdpUplinkMessage message) {
            try {
                if (message.getDeviceId() == null || message.getDeviceName() == null) {
                    udpServer.sendResponse(ctx, sender, "REGISTER", "ERROR",
                            "Missing device ID or name", null);
                    return;
                }

                // 创建设备对象
                Device device = Device.builder()
                        .deviceId(message.getDeviceId())
                        .deviceName(message.getDeviceName())
                        .deviceType(message.getDeviceType())
                        .firmwareVersion(message.getFirmwareVersion())
                        .ipAddress(sender.getHostString())
                        .port(sender.getPort())
                        .status("ONLINE")
                        .lastHeartbeat(LocalDateTime.now())
                        .build();

                // 发送注册消息给设备管理器Actor
                deviceManagerActor.tell(
                        new RegisterDevice(device, sender.getHostString(), sender.getPort()),
                        ActorRef.noSender()
                );

                // 发送响应
                udpServer.sendResponse(ctx, sender, "REGISTER", "SUCCESS",
                        "Device registered successfully", device.getDeviceId());
            } catch (Exception e) {
                log.error("Error handling device registration: {}", e.getMessage());
                udpServer.sendResponse(ctx, sender, "REGISTER", "ERROR",
                        "Registration failed: " + e.getMessage(), null);
            }
        }

        /**
         * 处理设备数据上报
         */
        private void handleData上报(ChannelHandlerContext ctx, InetSocketAddress sender, UdpUplinkMessage message) {
            try {
                if (message.getDeviceId() == null || message.getDataType() == null || message.getData() == null) {
                    udpServer.sendResponse(ctx, sender, "DATA", "ERROR",
                            "Missing device ID, data type or data", null);
                    return;
                }

                // 创建设备数据对象
                DeviceData deviceData = DeviceData.builder()
                        .deviceId(message.getDeviceId())
                        .dataType(message.getDataType())
                        .dataValue(objectMapper.writeValueAsString(message.getData()))
                        .timestamp(LocalDateTime.now())
                        .build();

                // 发送数据消息给设备管理器Actor
                deviceManagerActor.tell(
                        new DeviceDataMessage(deviceData),
                        ActorRef.noSender()
                );

                // 发送响应
                udpServer.sendResponse(ctx, sender, "DATA", "SUCCESS",
                        "Data received successfully", null);
            } catch (Exception e) {
                log.error("Error handling device data: {}", e.getMessage());
                udpServer.sendResponse(ctx, sender, "DATA", "ERROR",
                        "Failed to process data: " + e.getMessage(), null);
            }
        }

        /**
         * 处理设备心跳
         */
        private void handleHeartbeat(ChannelHandlerContext ctx, InetSocketAddress sender, UdpUplinkMessage message) {
            try {
                if (message.getDeviceId() == null) {
                    udpServer.sendResponse(ctx, sender, "HEARTBEAT", "ERROR",
                            "Missing device ID", null);
                    return;
                }

                // 发送状态更新消息给设备管理器Actor
                deviceManagerActor.tell(
                        new DeviceStatusUpdate(
                                message.getDeviceId(),
                                "ONLINE",
                                sender.getHostString(),
                                sender.getPort()
                        ),
                        ActorRef.noSender()
                );

                // 发送响应
                udpServer.sendResponse(ctx, sender, "HEARTBEAT", "SUCCESS",
                        "Heartbeat received", null);
            } catch (Exception e) {
                log.error("Error handling heartbeat: {}", e.getMessage());
                udpServer.sendResponse(ctx, sender, "HEARTBEAT", "ERROR",
                        "Failed to process heartbeat: " + e.getMessage(), null);
            }
        }

        /**
         * 处理命令响应
         */
        private void handleCommandResponse(ChannelHandlerContext ctx, InetSocketAddress sender, UdpUplinkMessage message) {
            try {
                if (message.getDeviceId() == null || message.getCommandId() == null || message.getStatus() == null) {
                    udpServer.sendResponse(ctx, sender, "COMMAND_RESPONSE", "ERROR",
                            "Missing device ID, command ID or status", null);
                    return;
                }

                log.info("Command {} from device {} completed with status: {}",
                        message.getCommandId(), message.getDeviceId(), message.getStatus());

                // 这里可以添加命令响应处理逻辑，如更新命令状态等

                // 发送响应
                udpServer.sendResponse(ctx, sender, "COMMAND_RESPONSE", "SUCCESS",
                        "Command response received", null);
            } catch (Exception e) {
                log.error("Error handling command response: {}", e.getMessage());
                udpServer.sendResponse(ctx, sender, "COMMAND_RESPONSE", "ERROR",
                        "Failed to process command response: " + e.getMessage(), null);
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            log.error("UDP handler exception: {}", cause.getMessage());
            ctx.close();
        }
    }

    /**
     * UDP上行消息通用格式
     */
    @Getter
    @Setter
    public static class UdpUplinkMessage {
        private String type; // REGISTER, DATA, HEARTBEAT, COMMAND_RESPONSE
        private String deviceId;
        private String deviceName;
        private String deviceType;
        private String firmwareVersion;
        private String dataType;
        private Object data;
        private Long commandId;
        private String status;
    }

    /**
     * UDP下行消息通用格式
     */
    @Getter
    @Setter
    @lombok.Builder
    public static class UdpDownlinkMessage {
        private String type; // COMMAND, CONFIG
        private Long commandId;
        private String commandType;
        private String params;
        private LocalDateTime timestamp;
    }

    /**
     * UDP响应消息格式
     */
    @Getter
    @Setter
    @lombok.Builder
    public static class UdpResponse {
        private String type;
        private String status; // SUCCESS, ERROR
        private String message;
        private Object data;
        private LocalDateTime timestamp;
    }
}
    